using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Collections.Concurrent;
using System.Net.Http.Json;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using System.Web;

namespace SeataNet.Core.Discovery.Nacos
{
    /// <summary>
    /// Nacos配置中心接口
    /// </summary>
    public interface INacosConfigService
    {
        /// <summary>
        /// 获取配置
        /// </summary>
        Task<string?> GetConfigAsync(string dataId, string? group = null);

        /// <summary>
        /// 发布配置
        /// </summary>
        Task<bool> PublishConfigAsync(string dataId, string content, string? group = null, string? type = null);

        /// <summary>
        /// 删除配置
        /// </summary>
        Task<bool> RemoveConfigAsync(string dataId, string? group = null);

        /// <summary>
        /// 添加配置监听器
        /// </summary>
        Task AddListenerAsync(string dataId, Action<string> callback, string? group = null);

        /// <summary>
        /// 移除配置监听器
        /// </summary>
        Task RemoveListenerAsync(string dataId, string? group = null);
    }

    /// <summary>
    /// Nacos配置中心实现
    /// </summary>
    public class NacosConfigService : INacosConfigService, IDisposable
    {
        private readonly ILogger<NacosConfigService> _logger;
        private readonly NacosConfiguration _configuration;
        private readonly HttpClient _httpClient;
        private readonly ConcurrentDictionary<string, string> _configCache;
        private readonly ConcurrentDictionary<string, string> _md5Cache;
        private readonly ConcurrentDictionary<string, Action<string>> _listeners;
        private readonly SemaphoreSlim _initLock;
        private readonly CancellationTokenSource _cancellationTokenSource;
        private string? _accessToken;
        private DateTime _tokenExpireTime;
        private bool _disposed;
        private Task? _longPollingTask;

        public NacosConfigService(
            ILogger<NacosConfigService> logger,
            IOptions<NacosConfiguration> configuration,
            IHttpClientFactory httpClientFactory)
        {
            _logger = logger;
            _configuration = configuration.Value;
            _httpClient = httpClientFactory.CreateClient("NacosClient");
            _configCache = new ConcurrentDictionary<string, string>();
            _md5Cache = new ConcurrentDictionary<string, string>();
            _listeners = new ConcurrentDictionary<string, Action<string>>();
            _initLock = new SemaphoreSlim(1, 1);
            _cancellationTokenSource = new CancellationTokenSource();

            ConfigureHttpClient();

            // 确保缓存目录存在
            if (_configuration.EnableConfigCache && !Directory.Exists(_configuration.ConfigCacheDir))
            {
                Directory.CreateDirectory(_configuration.ConfigCacheDir);
            }
        }

        private void ConfigureHttpClient()
        {
            _httpClient.Timeout = TimeSpan.FromMilliseconds(_configuration.ConfigLongPollTimeout + 10000);
        }

        public async Task<string?> GetConfigAsync(string dataId, string? group = null)
        {
            group ??= _configuration.GroupName;
            var configKey = GetConfigKey(dataId, group);

            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/cs/configs?" +
                          $"dataId={HttpUtility.UrlEncode(dataId)}&" +
                          $"group={HttpUtility.UrlEncode(group)}&" +
                          $"tenant={HttpUtility.UrlEncode(_configuration.Namespace)}";

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    url += $"&accessToken={HttpUtility.UrlEncode(_accessToken)}";
                }

                var response = await _httpClient.GetAsync(url);

                if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                {
                    _logger.LogWarning($"配置不存在: {configKey}");
                    return null;
                }

                response.EnsureSuccessStatusCode();
                var content = await response.Content.ReadAsStringAsync();

                // 缓存配置
                _configCache[configKey] = content;
                _md5Cache[configKey] = CalculateMd5(content);

                // 保存到本地缓存文件
                if (_configuration.EnableConfigCache)
                {
                    await SaveConfigCacheAsync(dataId, group, content);
                }

                _logger.LogInformation($"获取配置成功: {configKey}");
                return content;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"获取配置失败: {configKey}");

                // 尝试从缓存获取
                if (_configCache.TryGetValue(configKey, out var cachedContent))
                {
                    _logger.LogWarning($"使用缓存的配置: {configKey}");
                    return cachedContent;
                }

                // 尝试从本地文件获取
                if (_configuration.EnableConfigCache)
                {
                    var localContent = await LoadConfigCacheAsync(dataId, group);
                    if (!string.IsNullOrEmpty(localContent))
                    {
                        _logger.LogWarning($"使用本地缓存的配置: {configKey}");
                        return localContent;
                    }
                }

                if (_configuration.FailFast)
                {
                    throw;
                }

                return null;
            }
        }

        public async Task<bool> PublishConfigAsync(string dataId, string content, string? group = null, string? type = null)
        {
            group ??= _configuration.GroupName;
            type ??= "text";

            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/cs/configs";

                var parameters = new Dictionary<string, string>
                {
                    { "dataId", dataId },
                    { "group", group },
                    { "tenant", _configuration.Namespace },
                    { "content", content },
                    { "type", type }
                };

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    parameters["accessToken"] = _accessToken;
                }

                var formContent = new FormUrlEncodedContent(parameters);
                var response = await _httpClient.PostAsync(url, formContent);
                response.EnsureSuccessStatusCode();

                var result = await response.Content.ReadAsStringAsync();
                var success = result.Trim().Equals("true", StringComparison.OrdinalIgnoreCase);

                if (success)
                {
                    _logger.LogInformation($"发布配置成功: {GetConfigKey(dataId, group)}");
                }

                return success;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"发布配置失败: {GetConfigKey(dataId, group)}");
                return false;
            }
        }

        public async Task<bool> RemoveConfigAsync(string dataId, string? group = null)
        {
            group ??= _configuration.GroupName;
            var configKey = GetConfigKey(dataId, group);

            try
            {
                await EnsureAuthenticatedAsync();

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/cs/configs?" +
                          $"dataId={HttpUtility.UrlEncode(dataId)}&" +
                          $"group={HttpUtility.UrlEncode(group)}&" +
                          $"tenant={HttpUtility.UrlEncode(_configuration.Namespace)}";

                if (!string.IsNullOrEmpty(_accessToken))
                {
                    url += $"&accessToken={HttpUtility.UrlEncode(_accessToken)}";
                }

                var response = await _httpClient.DeleteAsync(url);
                response.EnsureSuccessStatusCode();

                var result = await response.Content.ReadAsStringAsync();
                var success = result.Trim().Equals("true", StringComparison.OrdinalIgnoreCase);

                if (success)
                {
                    _configCache.TryRemove(configKey, out _);
                    _md5Cache.TryRemove(configKey, out _);
                    _logger.LogInformation($"删除配置成功: {configKey}");
                }

                return success;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"删除配置失败: {configKey}");
                return false;
            }
        }

        public async Task AddListenerAsync(string dataId, Action<string> callback, string? group = null)
        {
            group ??= _configuration.GroupName;
            var configKey = GetConfigKey(dataId, group);

            _listeners[configKey] = callback;

            // 立即获取一次配置
            var config = await GetConfigAsync(dataId, group);
            if (!string.IsNullOrEmpty(config))
            {
                callback(config);
            }

            // 确保长轮询任务已启动
            EnsureLongPollingStarted();

            _logger.LogInformation($"添加配置监听器: {configKey}");
        }

        public Task RemoveListenerAsync(string dataId, string? group = null)
        {
            group ??= _configuration.GroupName;
            var configKey = GetConfigKey(dataId, group);

            _listeners.TryRemove(configKey, out _);
            _logger.LogInformation($"移除配置监听器: {configKey}");

            return Task.CompletedTask;
        }

        private void EnsureLongPollingStarted()
        {
            if (_longPollingTask != null)
            {
                return;
            }

            lock (this)
            {
                if (_longPollingTask != null)
                {
                    return;
                }

                _longPollingTask = Task.Run(async () => await LongPollingAsync());
                _logger.LogInformation("启动配置长轮询任务");
            }
        }

        private async Task LongPollingAsync()
        {
            while (!_disposed && !_cancellationTokenSource.Token.IsCancellationRequested)
            {
                try
                {
                    if (!_listeners.Any())
                    {
                        await Task.Delay(1000, _cancellationTokenSource.Token);
                        continue;
                    }

                    await EnsureAuthenticatedAsync();

                    var listeningConfigs = new StringBuilder();
                    var index = 0;

                    foreach (var listener in _listeners.Keys)
                    {
                        var parts = listener.Split('|');
                        if (parts.Length != 2) continue;

                        var dataId = parts[0];
                        var group = parts[1];

                        if (!_md5Cache.TryGetValue(listener, out var md5))
                        {
                            md5 = string.Empty;
                        }

                        listeningConfigs.Append(dataId).Append((char)2);
                        listeningConfigs.Append(group).Append((char)2);
                        listeningConfigs.Append(md5).Append((char)2);
                        listeningConfigs.Append(_configuration.Namespace).Append((char)1);

                        index++;
                    }

                    if (listeningConfigs.Length == 0)
                    {
                        await Task.Delay(1000, _cancellationTokenSource.Token);
                        continue;
                    }

                    var serverAddress = GetAvailableServer();
                    var url = $"{serverAddress}/nacos/v1/cs/configs/listener";

                    var parameters = new Dictionary<string, string>
                    {
                        { "Listening-Configs", listeningConfigs.ToString() }
                    };

                    if (!string.IsNullOrEmpty(_accessToken))
                    {
                        parameters["accessToken"] = _accessToken;
                    }

                    var content = new FormUrlEncodedContent(parameters);

                    using var cts = CancellationTokenSource.CreateLinkedTokenSource(_cancellationTokenSource.Token);
                    cts.CancelAfter(TimeSpan.FromMilliseconds(_configuration.ConfigLongPollTimeout + 10000));

                    var response = await _httpClient.PostAsync(url, content, cts.Token);
                    response.EnsureSuccessStatusCode();

                    var changedConfigs = await response.Content.ReadAsStringAsync();

                    if (!string.IsNullOrWhiteSpace(changedConfigs))
                    {
                        await ProcessChangedConfigsAsync(changedConfigs);
                    }
                }
                catch (OperationCanceledException)
                {
                    // 正常的超时或取消，继续下一轮
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "配置长轮询异常");
                    await Task.Delay(5000, _cancellationTokenSource.Token);
                }
            }

            _logger.LogInformation("配置长轮询任务已停止");
        }

        private async Task ProcessChangedConfigsAsync(string changedConfigs)
        {
            var lines = changedConfigs.Split(new[] { (char)1 }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var line in lines)
            {
                var parts = line.Split(new[] { (char)2 }, StringSplitOptions.RemoveEmptyEntries);
                if (parts.Length < 2) continue;

                var dataId = parts[0];
                var group = parts[1];
                var configKey = GetConfigKey(dataId, group);

                try
                {
                    var newConfig = await GetConfigAsync(dataId, group);

                    if (newConfig != null && _listeners.TryGetValue(configKey, out var callback))
                    {
                        _logger.LogInformation($"配置已变更: {configKey}");
                        callback(newConfig);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, $"处理配置变更失败: {configKey}");
                }
            }
        }

        private async Task EnsureAuthenticatedAsync()
        {
            if (string.IsNullOrEmpty(_configuration.Username) || string.IsNullOrEmpty(_configuration.Password))
            {
                return;
            }

            if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
            {
                return;
            }

            await _initLock.WaitAsync(_cancellationTokenSource.Token);
            try
            {
                if (!string.IsNullOrEmpty(_accessToken) && DateTime.Now < _tokenExpireTime)
                {
                    return;
                }

                var serverAddress = GetAvailableServer();
                var url = $"{serverAddress}/nacos/v1/auth/login";

                var parameters = new Dictionary<string, string>
                {
                    { "username", _configuration.Username },
                    { "password", _configuration.Password }
                };

                var content = new FormUrlEncodedContent(parameters);
                var response = await _httpClient.PostAsync(url, content, _cancellationTokenSource.Token);
                response.EnsureSuccessStatusCode();

                var result = await response.Content.ReadFromJsonAsync<NacosLoginResponse>(_cancellationTokenSource.Token);

                if (result != null && !string.IsNullOrEmpty(result.AccessToken))
                {
                    _accessToken = result.AccessToken;
                    _tokenExpireTime = DateTime.Now.AddSeconds(result.TokenTtl - 60);
                    _logger.LogInformation("Nacos认证成功");
                }
            }
            finally
            {
                _initLock.Release();
            }
        }

        private string GetAvailableServer()
        {
            return _configuration.ServerAddresses.FirstOrDefault()
                   ?? throw new InvalidOperationException("未配置Nacos服务器地址");
        }

        private string GetConfigKey(string dataId, string group)
        {
            return $"{dataId}|{group}";
        }

        private string CalculateMd5(string content)
        {
            using var md5 = MD5.Create();
            var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(content));
            return BitConverter.ToString(hash).Replace("-", "").ToLower();
        }

        private async Task SaveConfigCacheAsync(string dataId, string group, string content)
        {
            try
            {
                var fileName = GetCacheFileName(dataId, group);
                var filePath = Path.Combine(_configuration.ConfigCacheDir, fileName);
                await File.WriteAllTextAsync(filePath, content);
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, $"保存配置缓存失败: {GetConfigKey(dataId, group)}");
            }
        }

        private async Task<string?> LoadConfigCacheAsync(string dataId, string group)
        {
            try
            {
                var fileName = GetCacheFileName(dataId, group);
                var filePath = Path.Combine(_configuration.ConfigCacheDir, fileName);

                if (File.Exists(filePath))
                {
                    return await File.ReadAllTextAsync(filePath);
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, $"加载配置缓存失败: {GetConfigKey(dataId, group)}");
            }

            return null;
        }

        private string GetCacheFileName(string dataId, string group)
        {
            var key = $"{dataId}_{group}_{_configuration.Namespace}";
            return $"{CalculateMd5(key)}.config";
        }

        public void Dispose()
        {
            if (_disposed)
            {
                return;
            }

            _disposed = true;

            _cancellationTokenSource.Cancel();
            _cancellationTokenSource.Dispose();
            _initLock.Dispose();
            _httpClient.Dispose();

            _longPollingTask?.Wait(TimeSpan.FromSeconds(5));
        }
    }
}
